package com.heima.service.Impl;

import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.message.NewsAutoScanConstants;
import com.heima.common.constants.wemedia.WemediaConstants;
import com.heima.common.exception.CustException;
import com.heima.mapper.WmMaterialMapper;
import com.heima.mapper.WmNewsMapper;
import com.heima.mapper.WmNewsMaterialMapper;
import com.heima.mapper.WmUserMapper;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.message.NewsUpOrDownConstants;
import com.heima.model.message.PublishArticleConstants;
import com.heima.model.threadlocal.WmThreadLocalUtils;
import com.heima.model.wemedia.dto.NewsAuthDTO;
import com.heima.model.wemedia.dto.WmNewsDTO;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.model.wemedia.vo.WmNewsVO;
import com.heima.service.WmNewsService;
import com.heima.model.wemedia.dto.WmNewsPageReqDTO;
import com.heima.model.wemedia.pojos.WmNews;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
  @Value("${file.oss.web-site}")
  String webSite;

  @Autowired RabbitTemplate rabbitTemplate;

  /**
   * 查询自媒体文章列表
   *
   * @param dto
   * @return
   */
  @Override
  public ResponseResult findList(WmNewsPageReqDTO dto) {
    // 参数检查
    dto.checkParam();
    WmUser user = WmThreadLocalUtils.getUser();
    if (user == null) {
      CustException.cust(AppHttpCodeEnum.NEED_LOGIN);
    }
    LambdaQueryWrapper<WmNews> wrapper = Wrappers.lambdaQuery();
    // 有关键字就按照关键字查询
    if (StringUtils.isNotBlank(dto.getKeyword())) {
      wrapper.like(WmNews::getTitle, dto.getKeyword());
    }
    // 按照频道id查询
    if (dto.getChannelId() != null) {
      wrapper.eq(WmNews::getChannelId, dto.getChannelId());
    }
    // 按照文章得状态
    if (dto.getStatus() != null) {
      wrapper.eq(WmNews::getStatus, dto.getStatus());
    }
    if (dto.getBeginPubDate() != null && dto.getEndPubDate() != null) {
      wrapper
          .ge(WmNews::getCreatedTime, dto.getBeginPubDate())
          .le(WmNews::getPublishTime, dto.getEndPubDate());
    }
    wrapper.eq(WmNews::getUserId, user.getId())
            .orderByDesc(WmNews::getCreatedTime);
    // 进行分页查询
    IPage<WmNews> resPage = new Page<>(dto.getPage(), dto.getSize());
    IPage<WmNews> page = this.page(resPage, wrapper);
    List<WmNews> list = page.getRecords();

    ResponseResult result =
        new PageResponseResult(dto.getPage(), dto.getSize(), page.getTotal(), list);
    result.setHost(webSite);
    return result;
  }

  /**
   * 发布文章
   *
   * @param dto
   * @return
   * @throws InvocationTargetException
   * @throws IllegalAccessException
   */
  @Transactional
  @Override
  public ResponseResult submitNews(WmNewsDTO dto)
      throws InvocationTargetException, IllegalAccessException {
    // 1.检查参数
    // 文章的内容
    if (StringUtils.isBlank(dto.getContent())) {
      CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "文章不能为空");
    }
    // 检查文章状态
    Short status = dto.getStatus();
    if (!WemediaConstants.WM_NEWS_DRAFT_STATUS.equals(status)
        && !WemediaConstants.WM_NEWS_SUMMIT_STATUS.equals(status)) {
      CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "文章状态异常");
    }
    // 从threadlocal中获取user,是否登录
    WmUser user = WmThreadLocalUtils.getUser();
    if (user == null) {
      CustException.cust(AppHttpCodeEnum.NEED_LOGIN, "请登录");
    }
    // 生成wmUser
    WmNews wmNews = new WmNews();
    // 将dto中的属性copy到wmUser中
    BeanUtils.copyProperties(dto, wmNews);
    if (WemediaConstants.WM_NEWS_TYPE_AUTO.equals(dto.getType())) {
      // 将封面的状态设置为null,因为数据库中存储的是无符号
      wmNews.setType(null);
    }
    // 将images数组转化为字符串
    List<String> images = dto.getImages();
    if (images.size() > 0) {
      wmNews.setImages(imageToStr(images));
    }

    // 保存用户的id
    wmNews.setUserId(user.getId());
    // 保存或者修改文章文章
    saveOrUpdateWmNews(wmNews);
    // 设置内容和封面得关联关系
    // 抽取内容中的图图片形成一个集合
    List<String> imagesList = paseContentImages(dto.getContent());
    // 关联文章内容中的图片和素材关系
    if (CollectionUtils.isNotEmpty(imagesList)) {
      saveRelativeInfo(imagesList, wmNews, WemediaConstants.COLLECT_MATERIAL);
    }
    //  关联文章封面中的图片和素材关系  封面可能是选择自动或者是无图
    if (status == WmNews.Status.SUBMIT.getCode()) {
      saveRelativeInfoForCover(dto, imagesList, wmNews);
    }
    // 文章发布完成后。发送消息到RabbitMQ中
    rabbitTemplate.convertAndSend(NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_QUEUE, wmNews.getId());
    log.info(
        "成功发送 待审核消息 ==> 队列:{}, 文章id:{}",
        NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_QUEUE,
        wmNews.getId());

    return ResponseResult.okResult();
  }

  //根据id查询文章信息
  @Override
  public ResponseResult findWmNewsById(Integer id) {
    //1 参数检查
    if (id == null) {
      CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
    }
    //2 执行查询
    WmNews wmNews = getById(id);
    if (wmNews == null) {
      CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
    }
    //3 返回结果
    ResponseResult result = ResponseResult.okResult(wmNews);
    result.setHost(webSite);
    return result;
  }
  /**
   * 根据id删除文章
   * @param id
   * @return
   */
  @Override
  public ResponseResult delNews(Integer id) {
    //1.检查参数
    if (id==null){
      CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"文章id为空");
    }

    //2.获取数据
    WmNews wmNews = this.getById(id);
    if (wmNews==null){
      CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"文章不存在");
    }
    //3.判断当前文章的状态  status==9  enable == 1
    Short status = wmNews.getStatus();
    if (WemediaConstants.WM_NEWS_PUBLISH_STATUS.equals(status)
            &&WemediaConstants.WM_NEWS_UP.equals(wmNews.getEnable()))
    {
      CustException.cust(AppHttpCodeEnum.DATA_NOT_ALLOW,"文章已上架不能删除");
    }
    //4.去除素材与文章的关系
    LambdaQueryWrapper<WmNewsMaterial> wrapper = Wrappers.lambdaQuery();
    wrapper.eq(WmNewsMaterial::getMaterialId,wmNews.getId());
    wmNewsMaterialMapper.delete(wrapper);

    //5.删除文章
    this.removeById(id);
    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
  }
  /**
   * 上下架
   * @param dto
   * @return
   */
  @Override
  public ResponseResult downOrUp(WmNewsDTO dto) {
    //参数检验
    if (dto==null){
      CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
    }
    //判断文章是否是上下架的状态
    Short enable = dto.getEnable();
    if (enable==null||(!WemediaConstants.WM_NEWS_DOWN.equals(enable)&&!WemediaConstants.WM_NEWS_UP.equals(enable)))
    {
      CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"文章的状态异常");
    }
    //根据id查询wmNews
      WmNews wmNews = getById(dto.getId());
      if (wmNews==null){
        CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"文章不存在");
      }
      //判断文章是否处于发布状态
      Short status = wmNews.getStatus();
      if (!status.equals(WmNews.Status.PUBLISHED.getCode())){
        CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"文章为发布");
      }
      //修改文章状态
      LambdaUpdateWrapper<WmNews> wrapper = Wrappers.lambdaUpdate();
      wrapper.eq(WmNews::getId,dto.getId())
              .set(WmNews::getEnable,dto.getEnable());
      update(wrapper);
    if (wmNews.getArticleId()!=null) {
      if(enable.equals(WemediaConstants.WM_NEWS_UP)){
        // 上架消息
        log.info("开始上架");
        rabbitTemplate.convertAndSend(NewsUpOrDownConstants.NEWS_UP_OR_DOWN_EXCHANGE,
                NewsUpOrDownConstants.NEWS_UP_ROUTE_KEY,wmNews.getArticleId());
        log.info("上架消息发布");
      }else {
        // 下架消息
        log.info("开始下架");
        rabbitTemplate.convertAndSend(NewsUpOrDownConstants.NEWS_UP_OR_DOWN_EXCHANGE,
                NewsUpOrDownConstants.NEWS_DOWN_ROUTE_KEY,wmNews.getArticleId());
        log.info("下架消息发出");
      }
    }

    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
  }
  /**
   *  关联文章封面中的图片和素材关系  封面可能是选择自动或者是无图
   * @param dto
   * @param imagesList
   * @param wmNews
   */
  private void saveRelativeInfoForCover(WmNewsDTO dto, List<String> imagesList, WmNews wmNews) {
  //获取封面的图片
    List<String> images = dto.getImages();
    //然后判断是否是自动提交图片
    if (WemediaConstants.WM_NEWS_TYPE_AUTO.equals(dto.getType())){

      int size = imagesList.size();
      //内容的图片大于3，我们截取前三张图片，并将wmnuser的type设置为3
      if (size>=3){
        images= imagesList.stream().limit(3).collect(Collectors.toList());
        wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
      }else if(size>0&&size<3){
        images= imagesList.stream().limit(1).collect(Collectors.toList());
        wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
      }
      else {
        wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);

      }
      if (images != null && images.size() > 0) {
        // 将图片集合 转为字符串  url1,url2,url3
        wmNews.setImages(imageToStr(images));
      }
      //修改我们的wmNews
      updateById(wmNews);

      //如果我们封面的集合不为空，我们先去除他的前缀，保存他的关联关系
      if(CollectionUtils.isNotEmpty(images)){
        images= images.stream().map(url -> url.replaceAll(webSite,"")).collect(Collectors.toList());
        saveRelativeInfo(images,wmNews,WemediaConstants.WM_IMAGE_REFERENCE);
      }
    }
  }

  @Autowired WmMaterialMapper wmMaterialMapper;

  /**
   * 关联文章中图片素材的关联关系
   *
   * @param images
   * @param wmNews
   * @param type
   */
  private void saveRelativeInfo(List<String> images, WmNews wmNews, Short type) {
    // 先根据图片列表和userid，查询对于的素材列表
    WmUser user = WmThreadLocalUtils.getUser(); // 获取登录用户
    LambdaQueryWrapper<WmMaterial> wrapper = Wrappers.lambdaQuery();
    wrapper.in(WmMaterial::getUrl, images).eq(WmMaterial::getUserId, user.getId());
    //从数据库查询到的素材列表
    List<WmMaterial> materialList = wmMaterialMapper.selectList(wrapper);
    if (CollectionUtils.isEmpty(materialList)) {
      CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "关联的素材不存在");
    }
    List<String> imagesList = new ArrayList<>();
    List<Integer> idList = new ArrayList<>();
    // 获取对于的素材和id列表
    for (WmMaterial wmMaterial : materialList) {
      imagesList.add(wmMaterial.getUrl());
      idList.add(wmMaterial.getId());
    }
    //      判断图片在素材中是否存在
    for (String image : images) {
      //
      if (!imagesList.contains(image)) {
        CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "关联的素不存在");
      }
    }
    //     批量插入到表中，保存关联关系
    wmNewsMaterialMapper.saveRelations(idList, wmNews.getId(), type);
  }

  /**
   * 从文章的内容中抽取图片
   *
   * @param content
   * @return
   */
  private List<String> paseContentImages(String content) {
    // 将文章内容先解析成list<Map>
    List<Map> array = JSONArray.parseArray(content, Map.class);
    List<String> collect =
        array.stream()
            .filter(
                map -> WemediaConstants.WM_NEWS_TYPE_IMAGE.equals((String) map.get("type"))) // 过滤
            .map(map -> (String) map.get("value")) // 读取其中的value部分
            .map(url -> url.replaceAll(webSite, "")) // 将前缀部分给去除了
            .collect(Collectors.toList()); // 将其收集成集合
    return collect;
  }
  /** 修改或者保存 */
  @Autowired WmNewsMaterialMapper wmNewsMaterialMapper;

  private void saveOrUpdateWmNews(WmNews wmNews) {
    wmNews.setSubmitedTime(new Date());
    if (wmNews.getId() == null) {
      // id为null就保存wmNews
      wmNews.setCreatedTime(new Date());
      save(wmNews);

    } else if (WemediaConstants.COLLECT_MATERIAL.equals(wmNews.getId())) {
      // 有关联关系，根据id去查询，删除关联关系
      LambdaQueryWrapper<WmNewsMaterial> wrapper = Wrappers.lambdaQuery();
      wmNewsMaterialMapper.delete(wrapper.eq(WmNewsMaterial::getNewsId, wmNews.getId()));
      this.updateById(wmNews);
    }
  }
  /**
   * 将images转化为字符串
   *
   * @param images
   * @return
   */
  private String imageToStr(List<String> images) {
    String collect =
        images.stream().map(url -> url.replaceAll(webSite, "")).collect(Collectors.joining(","));
    return collect;
  }
  /**
   * 查询文章的列表
   * @param dto
   * @return
   */
  @Autowired
  WmNewsMapper wmNewsMapper;
  @Override
  public ResponseResult findNewsAuthList(NewsAuthDTO dto) {
    //1.检查参数
    dto.checkParam();
    //记录当前页
    int currentPage = dto.getPage();
    //设置起始页
    dto.setPage((dto.getPage()-1)*dto.getSize());
    if(StringUtils.isNotBlank(dto.getTitle())){
      dto.setTitle("%"+dto.getTitle()+"%");
    }

    //2.分页查询
    List<WmNewsVO> wmNewsVoList = wmNewsMapper.findListAndPage(dto);
    //统计多少条数据
    long count = wmNewsMapper.findListCount(dto);

    //3.结果返回
    ResponseResult result = new PageResponseResult(currentPage, dto.getSize(), count, wmNewsVoList);
    result.setHost(webSite);
    return result;
  }

  /**
   * 查询文章详情
   * @param id
   * @return
   */
  @Autowired
  WmUserMapper wmUserMapper;
  @Override
  public ResponseResult findWmNewsVo(Integer id) {
    //1参数检查
    if(id == null){
      return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }
    //2.查询文章信息
    WmNews wmNews = getById(id);
    if(wmNews == null){
      return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
    }
    //3.查询作者
    WmUser wmUser = null;
    if(wmNews.getUserId() != null){
      wmUser = wmUserMapper.selectById(wmNews.getUserId());
    }

    //4.封装vo信息返回
    WmNewsVO wmNewsVo = new WmNewsVO();
    BeanUtils.copyProperties(wmNews,wmNewsVo);
    if(wmUser != null){
      wmNewsVo.setAuthorName(wmUser.getName());
    }
    ResponseResult responseResult = ResponseResult.okResult(wmNewsVo);
    responseResult.setHost(webSite);
    return responseResult;
  }

  /**
   * 自媒体文章人工审核
   * @param status 2  审核失败  4 审核成功
   * @param dto
   * @return
   */
  @Override
  public ResponseResult updateStatus(Short status, NewsAuthDTO dto) {
    //1.参数检查
    if(dto == null || dto.getId() == null){
      return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }
    //2.查询文章
    WmNews wmNews = getById(dto.getId());
    if(wmNews == null){
      return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
    }
    // 检查文章状态 不能为9  已发布
    if (wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode())) {
      CustException.cust(AppHttpCodeEnum.DATA_NOT_ALLOW,"文章已发布");
    }
    //3.修改文章状态
    wmNews.setStatus(status);
    if(StringUtils.isNotBlank(dto.getMsg())){
      wmNews.setReason(dto.getMsg());
    }
    updateById(wmNews);

    //  通知定时发布文章
    if (status.equals(WmNews.Status.PUBLISHED.getCode())){
      Long publishTime = wmNews.getPublishTime().getTime();
      Long nowTime = new Date().getTime();
      Long realTime = publishTime-nowTime;
      if (realTime<=0){
        //立即发布消息到发布文章队列
        rabbitTemplate.convertAndSend(PublishArticleConstants.PUBLISH_ARTICLE_EXCHANGE
                                          ,PublishArticleConstants.PUBLISH_ARTICLE_ROUTE_KEY
                                          ,wmNews.getId());
        log.info("人工审核通过的消息发出了,立即发布消息了");
      }else {

        //延迟队列发送消息
        rabbitTemplate.convertAndSend(PublishArticleConstants.PUBLISH_ARTICLE_EXCHANGE,
                              PublishArticleConstants.PUBLISH_ARTICLE_DEAD_ROUTE_KEY,
                              wmNews.getId(),
                              message -> {
          message.getMessageProperties().setExpiration(String.valueOf(realTime));
          return message;
                              });
        log.info("人工审核通过的消息发出了,延迟队列发布消息了");
      }
    }
    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
  }

}
